package com.cee.admin.config.shiro;

import com.cee.admin.common.GlobalExceptionHandler;
import com.cee.admin.config.shiro.jwt.JwtFilter;
import com.cee.admin.config.shiro.jwt.TokenService;
import org.apache.shiro.mgt.*;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    public static final int HASH_ITERATIONS = 1024;

    // 配置自定义Realm
    @Bean
    public ShiroRealm userRealm() {
        ShiroRealm shiroRealm = new ShiroRealm();
        //权限全路径匹配
        shiroRealm.setPermissionResolver(new WholePermissionResolver());
        return shiroRealm;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        Map<String, String> map = new LinkedHashMap<>(5);
        // 上传持久化文件到指定相对路径需要登录
        map.put("/api/v1/common/upload-permanent-file", "jwt");
        // 拦截登出接口，避免无法获取到currentUser而导致日志中没有该userId
        map.put("/auth/logout", "jwt");
        // 一些公共接口
        map.put("/api/v1/common/upload-file", "jwt");
        map.put("/api/v1/common/upload-files", "jwt");
        map.put("/api/v1/common/download", "jwt");
        map.put("/api/v1/common/**", "anon");
        map.put("/favicon.ico", "anon");
        //swagger文档地址放行
        map.put("/swagger-ui/**", "anon");
        map.put("/swagger-ui.html", "anon");
        map.put("/webjars/springfox-swagger-ui/**", "anon");
        map.put("/swagger-resources/**", "anon");
        map.put("/doc.html", "anon");
        map.put("/webjars/**", "anon");
        map.put("/v3/api-docs/**", "anon");


        // 请求进行验证相关接口放行
        map.put("/auth/**", "anon");
        // 驾驶舱放行
        map.put("/api/cockpit/**", "anon");

        //二维码数据接口放行
        map.put("/api/cfd/carbon/footprint/certificate/report/qrCode/data/get/**", "anon");


        // jwt拦截器
        map.put("/**", "jwt");
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        chainDefinition.addPathDefinitions(map);
        return chainDefinition;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(TokenService tokenService, GlobalExceptionHandler globalExceptionHandler) {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager());
        filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());
        LinkedHashMap<String, Filter> filterMap = new LinkedHashMap<>(1);
//        filterMap.put("jwt", new JwtFilter(tokenService, globalExceptionHandler, corsProperties));
        filterMap.put("jwt", new JwtFilter(tokenService, globalExceptionHandler));
        filterFactoryBean.setFilters(filterMap);
        return filterFactoryBean;
    }

    // 设置用于匹配密码的CredentialsMatcher
//    @Bean
//    public HashedCredentialsMatcher credentialsMatcher() {
//        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//        credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);  // 散列算法，这里使用更安全的sha256算法
//        credentialsMatcher.setStoredCredentialsHexEncoded(false);  // 数据库存储的密码字段使用HEX还是BASE64方式加密
//        credentialsMatcher.setHashIterations(HASH_ITERATIONS);  // 散列迭代次数
//        return credentialsMatcher;
//    }

    // 配置security并设置userReaml，避免xxxx required a bean named 'authorizer' that could not be found.的报错
    @Bean
    public SessionsSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        securityManager.setSubjectDAO(subjectDAO());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        //设置安全管理器
        attributeSourceAdvisor.setSecurityManager(securityManager());
        return attributeSourceAdvisor;
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole等shiro注解，会导致该方法无法映射请求，导致返回404。 加入这项配置能解决这个bug
         */
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        //defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 关闭session
     *
     * @return
     */
    @Bean
    public SessionManager sessionManager() {
        DefaultSessionManager defaultSessionManager = new DefaultSessionManager();
        defaultSessionManager.setSessionValidationSchedulerEnabled(false);
        return defaultSessionManager;
    }

    @Bean
    public SubjectDAO subjectDAO() {
        DefaultSubjectDAO defaultSubjectDAO = new DefaultSubjectDAO();
        defaultSubjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator());
        return defaultSubjectDAO;
    }

    /**
     * 关闭session
     *
     * @return
     */
    @Bean
    public SessionStorageEvaluator sessionStorageEvaluator() {
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        return defaultSessionStorageEvaluator;
    }
}
